<?php

/*

    MMetaverse - Open Metaverse Access Library

    Copyright (c) 2009 Mary Jane Soft - Marlon J. Manrique

    http://www.maryjanesoft.com
    http://www.marlonj.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General
    Public License along with this library; if not, write to the
    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

    $Id$

*/

/**
 * @package mjs.mmetaverse.client
 */

require_once dirname(__FILE__) . '/../llsd/MLLSDClient.class.php';
require_once dirname(__FILE__) . '/../inventory/MInventory.class.php';

require_once 'MCapabilities.class.php';
require_once 'MNetworkClient.class.php';

/**
 * Metaverse client 
 */
class MMetaverseClient {

    /**
     * Metaverse Server
     *
     * @var string
     */
    private $server;

    /**
     * Metaverse Server port
     *
     * @var <type>
     */
    private $port;

    /**
     * Security communications enabled
     *
     * @var boolean
     */
    private $secure;

    /**
     * Open a network client in login
     *
     * @var boolean
     */
    private $openNetworkClient;

    /**
     * Circuit Code
     *
     * @var integer
     */
    private $circuitCode;

    /**
     * Agent ID
     *
     * @var string
     */
    private $agentID;

    /**
     * Session ID
     *
     * @var string
     */
    private $sessionID;

    /**
     * LLSD Client to send requests
     *
     * @var MLLSDClient 
     */
    private $llsdClient;

    /**
     * Network client
     *
     * @var MNetworkClient
     */
    private $networkClient;

    /**
     * Seed Capability
     *
     * @var string
     */
    private $seedCapability;

    /**
     * Currrent Capabilities
     *
     * @var MCapabilities 
     */
    private $capabilities;

    /**
     * Current Inventory
     *
     * @var MInventory
     */
    private $inventory;

    /**
     * Create a metaverse client ready to connect with the server in the port
     * and using secure protocols, if security es enabled port value is not
     * use. 
     *
     * @param string $server Metaverse server name or ip
     * @param integer $port Metaverse server port
     * @param boolean $secure If true use https protocol, otherwise http
     * @param boolean $openNetworkClient If true open a network client in login
     *                otherwise use this client only for capabilities 
     */
    public function MMetaverseClient($server, $port, $secure = false,
        $openNetworkClient = true) {

        // Set attributes 
        $this->server = $server;
        $this->port = $port;
        $this->secure = $secure;
        $this->openNetworkClient = $openNetworkClient;

        // Create the llsd client
        $this->llsdClient = new MLLSDClient();
        $this->inventory = new MInventory($this);
    }

    /**
     * Return the web server uri according server and port values, if secure
     * attribute is true a https without port is returned otherwise a http
     * url with port is returned 
     *
     * @return string
     */
    private function getServerURI() {
        // Use HTTPS if secure is enabled 
        if($this->secure) {
            $protocol = 'https';
            $port = '';
        } else {
            $protocol = 'http';
            $port = ':' . $this->port;
        }
        
        // Return url
        return $protocol . '://' . $this->server . $port;
    }

    /**
     * Return the web server login uri
     *
     * @return string
     */
    private function getLoginURI() {
        return $this->getServerURI() . '/cgi-bin/login.cgi';
    }

    /**
     * Return the llsd client
     *
     * @return MLLSDClient 
     */
    public function getLLSDClient() {
        return $this->llsdClient;
    }

    /**
     * Send a llsd request to the url capabilitie
     *
     * @param string $url Capabilitie url
     * @param string $request llsd XML
     * @return array
     */
    public function sendRequest($url, $request) {
        return $this->llsdClient->sendRequest($url, $request);
    }

    /**
     * Login to the server
     *
     * @param string $firstname
     * @param string $lastname
     * @param string $password
     */
    public function login($firstname, $lastname, $password) {
        // Get the login request
        $request = MLLSD::createLoginRequest($firstname, $lastname, $password);

        // Send the request
        $response = $this->sendRequest($this->getLoginURI(), $request);

        // If llsd login value not available, response unknown
        if(!isset($response[0]['login'])) {
            throw new Exception('Unknown Response');
        }
        
        // If login is true, grab login values
        if($response[0]['login'] == 'true') {
            $this->loadConnectionValues($response);
        } else {
            // Set the error message from server
            // or put a Login Invalid
            if(isset($response[0]['message'])) {
                $message = $response[0]['message'];
            } else {
                $message = 'Login Invalid';
            }
            
            // Throw error with the message
            throw new Exception($message);
        }

        // Open the network client only if request 
        if($this->openNetworkClient) {
            $this->openNetworkClient($response);
        }

        // Load inventory 
        $this->loadInventory($response);
    }

    /**
     * Load connection values 
     *
     * @param array $response Login Response 
     */
    private function loadConnectionValues(array $response) {
        // Get capability URL
        $this->seedCapability = $response[0]['seed_capability'];

        // Get Network info 
        $this->simIP = $response[0]['sim_ip'];
        $this->simPort = $response[0]['sim_port'];

        //  Get circuit info
        $this->circuitCode = (integer) $response[0]['circuit_code'];
        $this->agentID = $response[0]['agent_id'];
        $this->sessionID = $response[0]['session_id'];
    }

    /**
     * Load the inventory data
     *
     * @param array $response Login Response
     */
    private function loadInventory(array $response) {
        $this->inventory->load($response);
    }

    /**
     * Open Network client 
     */
    private function openNetworkClient() {
        // Create a network client
        $this->networkClient = new MNetworkClient($this->simIP,$this->simPort,
            $this->circuitCode,$this->agentID,$this->sessionID);

        // Do login 
        $this->networkClient->login();
    }

    /**
     * Return the capabilities for this client
     *
     * @return MCapabilities Current Capabilities 
     */
    public function getCapabilities() {
        // If the seed capability is not set, the user is not authenticate yet
        if(!isset($this->seedCapability)) {
            throw new Exception('Needs to login');
        }

        // If the capabilities are not set, create it 
        if(!isset($this->capabilities)) {
            $this->capabilities = new MCapabilities($this);
            $this->capabilities->requestValues($this->seedCapability);
        }

        // Return capabilities 
        return $this->capabilities;
    }

    /**
     * Returns the user inventory
     *
     * @return MInventory
     */
    public function getInventory() {
        return $this->inventory;
    }

    /**
     * Close connections 
     */
    public function logout() {
        // Logout from the network
        if(isset($this->networkClient))
            $this->networkClient->logout();

        // Remove capabilitities and networkclient 
        $this->seedCapability = null;
        $this->networkClient = null;
        $this->capabilities = null;
    }

    /**
     * Update Client.
     * Receive information from server
     */
    public function update()
    {
        // If a network client is open try to receive data 
        if(isset($this->networkClient)) {
            $this->networkClient->receivePacket();
        }
    }
}

?>
